<body>
提供一些允许nachos载入和执行运行在独立的虚地址空间上的单线程用户程序的类。
<p>
实验二题目：
<p>
1、实现文件系统调用(syscall.h中的creat、open、read、write、close和unlink)。你会在UserProcess.java中看到halt系统调用的代码。你也最好把其他系统调用的代码放到这里。注意你要实现的不是一个文件系统，而是给用户进程一个访问我们所实现的文件系统的能力。
<ol>
<li>我们在start.s中提供了在用户程序中进行系统调用所必需的汇编代码（阅读start.s；SYSCALLSTUB部分对每个系统调用产生汇编代码）。
<li>你需要保证用户程序错误不会影响到nachos内核；不应该有任何用户程序使操作系统崩溃（......）。换句话说，你必须保证用户程序不会传递违规参数给内核，使内核和其他用户进程崩溃。所以你应该保证，当用户进程违法操作时，会导致处理器抛出异常，从而这个进程会被立刻杀死并且其资源得到释放。
<li>你应该使halt系统调用只能被root进程调用——即第一个系统中的进程。如果另一个进程试图调用halt(),这个系统调用应该被忽略并且立刻返回。
<li>因为内存地址传递给系统调用是通过虚拟地址，你需要使用UserProcess.readVirtualMemory和UserProcess.writeVirtualMemory来在用户进程和内核中传递内存信息。
<li>用户进程以带空终止字符的方式在虚地址空间中存储文件名和其他字符串参数。作为参数的字符串最大长度为256字节。
<li>当一个系统调用希望返回执行错误的情况给用户时，应该返回-1（而不是在内核中抛出异常）。在其他时候，系统调用应该返回syscall.h中约定的值。
<li>当一个用户进程启动，文件标识符0和1对应着标准输入和标准输出。使用UserKernel.console.openForReading()和UserKernel.console.openForWriting()可以很方便的得到。
<li>用户进程是被允许关闭这些文件操作符的，就像由open返回的文件操作符一样。如果他们关闭了，不要担心重新打开他们的问题。
<li>一个为UNIX系统使用的StubFileSystem的接口已经提供给你。这个接口在machine.FileSystem.java中。你可以通过静态域ThreadedKernel.fileSystem来访问这个文件系统（因为UserKernel继承自ThreadedKernel，你也可以通过UserKernel访问）。这个文件系统有能力访问test目录，这一点在实现exec系统调用时十分有用。你不需要实现任何文件系统。为了在系统调用中实现对应的功能，你应该仔细阅读FileSystem和StubFileSytem，并且系统是如何使用这两个文件系统的。
<li>不要实现任何性质的锁。这是文件系统应该负责的。如果ThreadedKernel.fileSystem.open()返回了一个不为null的文件，用户进程就被允许访问这个文件；否则，应该返回一个错误。你不需要担心多个进程同时访问一个文件的细节。stub文件系统应该帮你处理这些细节。
<li>你的实现应该能在一个进程中同时打开16个文件，包括stdin和stdout。进程打开的每个文件应该有一个与之相关的唯一的文件标识符(阅读syscall.h获知更多细节)。文件标识符应该是一个非负整数，表示该文件在当前进程的文件表中的索引。注意当文件关闭时，一个给定的文件标识符是可重用的，不同进程可使用相同的文件标识符来指向不同的文件。
</ol>
<p>
2、实现对多道程序的支持。我们给你的代码在同一时间是被限制运行一个进程的；你的工作是在多个进程时它也能正常工作。
<ol>
<li>想一个分配物理内存的办法，使不同进程不会覆盖彼此的内存使用。注意用户程序不会使用malloc()以及free()函数，这意味着用户进程没有动态内存分配的需求（即没有堆）。这意味着在进程创建时你就知道完整的内存需求；你可以分配固定的页给进程的栈空间，8页已经足够了。
<li>我们建议对全局空闲的物理页维持一个链表（这个链表可以作为UserKernel类的一部分），在必要时对该表的访问使用同步技术。你的解决方案应该通过为新进程分配页尽可能充分利用内存。这意味着只在连续区域分配页是不可接受的。你的解决方案应该在空闲内存池中充分利用外碎片。
<li>同时在执行exit系统调用时，进程的内存区域会被释放。（无论是正常退出或者异常退出）
<li>修改UserProcess.readVirtualMemory 和UserProcess.writeVirtualMemory，这两个函数在内核和用户虚地址空间中拷贝数据，使修改后的函数能在多进程环境下正常工作。
<li>MIPS物理内存通过Machine.processor().getMemory()访问；物理页的总数可以通过Machine.processor().getNumPhysPage()得知。你应该为每个用户进程维持一个pageTable,这个页表用来在虚地址和物理地址之间进行映射。TranslationEntry类代表了一个单向的虚拟到物理页的转化单元。
<li>如果COFFsection中该页被标记为只读，属性 TranslationEntry.readOnly应该为真。你可以通过使用方法CoffSection.isReadOnly()得知。
<li>注意该方法失败时不该抛出任何异常，相反地，他应该总是返回转化的数目。（即便是0）
<li>修改UserProcess.loadSection()以便分配需要的内存页（页的数目基于程序的大小），在此过程中应使用在以你决定使用的内存分配策略。这个方法应该为进程维护好pageTable的结构，以便进程载入何时的物理内存页。如果新的用户进程不能加载入任何用户内存，exec()应该返回一个错误。
<li>注意用户线程在上下文切换时已经保存和恢复机器的状态，这也包括进程的状态。所以你不必负责这些细节。
</ol>
<p>
3、实现系统调用（声明在syscall.h中的exec、join、exit）
<ol>
<li>再次说明，在exec和join时，所有在寄存器中的地址都是虚拟地址。你应该使用readVirtualMemeory和readVirtualMemoryString方法在内核和用户进程间存取数据。
<li>当然，你必须对你的实现给出测试。
<li>注意子进程的状态对父进程而言是private的，这意味着父子进程不会共享内存和文件标识符。注意2个进程可以同时打开相同的文件。举个例子，所有的进程都有文件标识符0和1对应着控制台，正如以上描述的那样。
<li>不像KThread.join(),只有父进程可以将子进程join进来。举个栗子，如果A执行了B而B执行了C，A不允许C join，但B可以。
<li>join携带了processID作为参数，用来唯一标识一个进程。pid应该是一个唯一的正整数，当进程创建时赋值给进程（即便这个项目中pid只用来join，之后的项目pid的唯一性对整个系统来说很重要）。最简单生成pid的方式就是维持一个static类型计数器，该值表示下一个进程的pid。因为pid是int，如果太多进程在系统中可能导致溢出。对于当前的project不用担心相关细节，即假定pid的计数器不会溢出。
<li>当一个进程调用exit(),这个线程应该被立刻终止，并且进程应该清理和他相关的任何状态（清理内存、关闭打开的文件）。当进程不正常退出时也该如此。
<li>进程退出的状态应该告知父进程，以防父进程调用join系统调用。进程不正常退出状态的设定由你决定。为了实现join方法，如果exit系统调用以任何参数执行，子进程都应正常退出，当内核杀死进程时可能会不正常。
<li>最后一个调用exit的进程应该通过Kernel.kernel.terminate()来终止机器（注意只有root进程才有调用halt系统调用的权限，但最后一个退出的进程应该直接调用Kernel.kernel.terminate()）
</ol>
<p>
4、实现一个彩票调度（在threads包LotteryScheduler中）.注意这个类继承了PriorityScheduler，你应该有能力重用这个类的大部分功能性代码。彩票调度类不应该添加大量的代码。主要差异在于：机器从队列中得到一个中彩票的线程，而不是队列中优先级最大的。你的彩票调度应该实现优先级捐赠。（注意因为这个彩票调度，优先级倒转不能导致饥饿。然而，你的调度必须实现优先级捐赠。）
<ol>
<li>在彩票调度中，实现的不是简单而优先级捐赠，而是等待的线程要将自己的tickets交给他们正在等的线程。不像标准的优先级调度，一个等待的线程总是添加他们的tickets数目给他们正在等的线程。这意味着，一个线程的tickets数目是其等待队列中线程的总和，而不是最大值。保证该点的正确实现。
<li>你的解决方案应该在上百万票时正确实现（不要用数组存每一张票）。
<li>当调用LotteryScheduler.increasePriority()时，一个进程持有的tickets应该增加1张。decreasePriority有相反的效果。
<li>系统中票的总数应保证不超过Integer.MAX_VALUE,最大优先级也是Integer.MAX_VALUE，而不是7.如果你愿意，最小优先级也可以是1
</ol>
Provides classes that allow Nachos to load and execute single-threaded user
programs in separate address spaces.
</body>
